#ifndef _CANARY2_HTTP_REQUEST_BUILDER_H_
#define _CANARY2_HTTP_REQUEST_BUILDER_H_

#include "message_details.h"
#include "request.h"
#include <memory>
#include "version.h"
#include "method.h"
#include <utility>
#include <boost/lexical_cast.hpp>

namespace wilson::http
{
#define WILSON_OPT_NOEXCEPT_BODY(exp) noexcept(noexcept(exp)) { (exp); }

    template<typename MessageType>
    class basic_request_builder
    {
    public:
        using message_type  = MessageType;
        using string_type   = typename message_type::string_type;
        using generate_type = basic_request<message_type>;
        using storage_type  = typename message_type::storage_type;

    public:
        basic_request_builder() : m_data{ ::std::make_shared<message_type>() } {}
        basic_request_builder(storage_type&& stor) 
            : m_data{ ::std::make_shared<message_type>(::std::move(stor)) } {}
        basic_request_builder(::std::shared_ptr<storage_type> stor) 
            : m_data{ ::std::make_shared<message_type>(::std::move(stor)) } {}

        void set_version(version s)     noexcept { m_data->m_version  = s;   }
        void set_method(method s)       noexcept { m_data->m_method   = s;   }
        void set_body(auto s)    WILSON_OPT_NOEXCEPT_BODY(m_data->m_body     = string_type(::std::move(s)));
        void set_url(auto s)     WILSON_OPT_NOEXCEPT_BODY(m_data->m_url      = string_type(::std::move(s)));
        void set_path(auto s)    WILSON_OPT_NOEXCEPT_BODY(m_data->m_path     = string_type(::std::move(s)));
        void set_query(auto s)   WILSON_OPT_NOEXCEPT_BODY(m_data->m_query    = string_type(::std::move(s)));
        void set_fragment(auto s)WILSON_OPT_NOEXCEPT_BODY(m_data->m_fragment = string_type(::std::move(s)));
        void set_keep_alive(bool s)     noexcept { m_data->m_keep_alive = s; }
        void set_web_socket(bool s)     noexcept { m_data->m_web_socket = s; }

        template<typename Str1, typename Str2>
        requires (::std::convertible_to<Str1, string_type>
               && ::std::convertible_to<Str2, string_type>)
        void set_header(Str1&& key, Str2&& value)
        {
            m_data->m_headers.emplace( ::std::make_pair(
                string_type{::std::forward<decltype(key)>(key)}, 
                string_type{::std::forward<decltype(value)>(value)}
            ));

            //m_data->m_headers.emplace( ::std::make_pair(
            //    ::boost::lexical_cast<string_type>(::std::forward<decltype(key)>(key)), 
            //    ::boost::lexical_cast<string_type>(::std::forward<decltype(value)>(value))
            //));
        }

        generate_type generate_request() noexcept { return { m_data }; }
        auto generate() noexcept { return generate_request(); }
        auto& storage() noexcept { return m_data->storage(); }

    private: 
        ::std::shared_ptr<message_type> m_data;
    };

#undef WILSON_OPT_NOEXCEPT_BODY

    namespace server { using request_builder = basic_request_builder<details::message_from_net>; }
    namespace client { using request_builder = basic_request_builder<details::message_to_net>; }
}

#endif
